package com.xbongbong.saas.analytical.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FlowStatusEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.AttrLenthConstant;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ContractErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.InstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.OutstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SettleAccountErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.saas.analytical.SaasValidateAnalyticalService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.domain.entity.SettleAccountsEntity;
import com.xbongbong.saas.domain.entity.ext.InstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.InventoryEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.domain.entity.ext.RefundEntityExt;
import com.xbongbong.saas.enums.InstockTypeEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.InventoryEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.PurchaseInstockEnum;
import com.xbongbong.saas.enums.business.RefundEnum;
import com.xbongbong.saas.enums.business.RefundInstockEnum;
import com.xbongbong.saas.enums.product.InstockProductEnum;
import com.xbongbong.saas.help.AnalyticalHelp;
import com.xbongbong.saas.help.SaasInstockHelp;
import com.xbongbong.saas.help.SaasProductHelp;
import com.xbongbong.saas.help.SaveFormatHelp;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InventoryModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.RefundModel;
import com.xbongbong.saas.model.SettleAccountsModel;
import com.xbongbong.saas.service.toolbox.help.UserTeamHelp;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.util.ProductUtil;
import com.xbongbong.util.ValidateUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author 吴峰
 * @date 2021/07/15 16:46
 */
@Service("instockAnalyticalServiceImpl")
public class InstockValidateAnalyticalServiceImpl implements SaasValidateAnalyticalService {
    @Resource
    private InstockModel instockModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private SaasProductHelp saasProductHelp;
    @Resource
    private InventoryModel inventoryModel;
    @Resource
    private UserTeamHelp userTeamHelp;
    @Resource
    private RefundModel refundModel;
    @Resource
    private SaasInstockHelp saasInstockHelp;
    @Resource
    private ProductModel productModel;
    @Resource
    private SettleAccountsModel settleAccountsModel;
    @Resource
    private AnalyticalHelp analyticalHelp;

    @Override
    public List<Integer> type() {
        return Arrays.asList(XbbRefTypeEnum.INSTOCK.getCode(), XbbRefTypeEnum.PURCHASE_INSTOCK.getCode(), XbbRefTypeEnum.REFUND_INSTOCK.getCode(),
                XbbRefTypeEnum.OTHER_INSTOCK.getCode(), XbbRefTypeEnum.PRODUCTION_INSTOCK.getCode(), XbbRefTypeEnum.RETURNED_MATERIEL_INSTOCK.getCode());

    }

    @Override
    public void analyticalData(ValidateDataDTO validateDataDTO) throws XbbException {
        beforeSave(validateDataDTO);
        //没有负责团队的 单负责人
        userTeamHelp.checkPermissionOptionTeam(validateDataDTO);
    }
    
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        JSONObject data = validateDataDTO.getData();
        Long dataId = validateDataDTO.getDataId();
        Long taskId = validateDataDTO.getTaskId();
        String corpid = validateDataDTO.getCorpid();
        Integer businessType = validateDataDTO.getBusinessType();
        InstockTypeEnum instockTypeEnum;
        Integer type = data.getInteger(InstockEnum.TYPE.getAttr());
        if (Objects.isNull(type)) {
            instockTypeEnum = InstockTypeEnum.getByStockBusinessType(businessType);
            data.put(InstockEnum.TYPE.getAttr(), String.valueOf(instockTypeEnum.getCode()));
        } else {
            instockTypeEnum = InstockTypeEnum.getByCode(type);
        }
        switch (instockTypeEnum) {
            case PURCHASE_INSTOCK:
                SaveFormatHelp.formatLinkBusiness4Save(data, PurchaseInstockEnum.SUPPLIER_ID.getAttr(), PurchaseInstockEnum.SUPPLIER_ID_LINKED_TEXT.getAttr(), PurchaseInstockEnum.SUPPLIER_ID.getFieldType());
            case OTHER_INSTOCK:
            case FINSHED_PRODUCT_INSTOCK:
                JSONArray productJsonArray = data.getJSONArray(InstockEnum.PRODUCT.getAttr());
                if (CollectionsUtil.isEmpty(productJsonArray)) {
                    break;
                }
                String productAttr = InstockProductEnum.PRODUCT.getAttr();
                // 将入库的产品ID取出
                List<Long> productIdList = new ArrayList<>();
                for (int i = 0; i < productJsonArray.size(); i++) {
                    JSONObject productJson = productJsonArray.getJSONObject(i);
                    if (productJson.containsKey(productAttr)) {
                        Long productId = productJson.getLong(productAttr);
                        productIdList.add(productId);
                    }
                }
                // 查询入库的产品信息
                List<ProductEntityExt> productExtList = productModel.getProductListByIdIn(corpid, productIdList, DelEnum.NORMAL.getDel());
                // 是否启用批次的Attr
                String enableBatchShelfLifeAttr = ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr();
                // 是否启用批次的SaasAttr
                String enableBatchShelfLifeSaasAttr = ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr();
                for (ProductEntityExt productExt : productExtList) {
                    Long productExtId = productExt.getId();
                    JSONObject productData = productExt.getData();
                    if (!productData.containsKey(enableBatchShelfLifeAttr)) {
                        continue;
                    }
                    // 跟新否启用批次字段
                    Integer enableBatchShelfLife = productData.getInteger(enableBatchShelfLifeAttr);
                    for (int i = 0; i < productJsonArray.size(); i++) {
                        JSONObject productJson = productJsonArray.getJSONObject(i);
                        if (productJson.containsKey(productAttr) && Objects.equals(productExtId, productJson.getLong(productAttr))) {
                            productJson.put(enableBatchShelfLifeSaasAttr, enableBatchShelfLife);
                        }
                    }
                }
                break;
            case RED_CONTRACT_INSTOCK:
                SaveFormatHelp.formatLinkBusiness4Save(data, RefundInstockEnum.CUSTOMER_ID.getAttr(), RefundInstockEnum.CUSTOMER_ID_LINKED_TEXT.getAttr(), RefundInstockEnum.CUSTOMER_ID.getFieldType());
                // 如果前端没传根据默认公式计算一下
                if (Objects.isNull(data.getDouble(RefundInstockEnum.PRODUCT_TOTAL.getAttr()))) {
                    JSONArray productArr = data.getJSONArray(RefundInstockEnum.PRODUCT.getAttr());
                    if (CollectionsUtil.isNotEmpty(productArr)) {
                        Double productTotal = 0D;
                        for (Object product : productArr) {
                            JSONObject productObj = (JSONObject) product;
                            if (Objects.nonNull(productObj.getDouble(SelectProductEnum.SUBTOTAL.getAttr()))) {
                                productTotal += productObj.getDouble(SelectProductEnum.SUBTOTAL.getAttr());
                            }
                        }
                        data.put(RefundInstockEnum.PRODUCT_TOTAL.getAttr(), productTotal);
                    } else {
                        data.put(RefundInstockEnum.PRODUCT_TOTAL.getAttr(), 0);
                    }
                }
                if (Objects.isNull(data.getDouble(RefundInstockEnum.OTHER_EXPENSE.getAttr()))) {
                    data.put(RefundInstockEnum.OTHER_EXPENSE.getAttr(), 0);
                }
                if (Objects.isNull(data.getDouble(RefundInstockEnum.TOTAL_MONEY.getAttr()))) {
                    data.put(RefundInstockEnum.TOTAL_MONEY.getAttr(), Arith.add(data.getDouble(RefundInstockEnum.PRODUCT_TOTAL.getAttr()), data.getDouble(RefundInstockEnum.OTHER_EXPENSE.getAttr())));
                }
                // 校验入库金额
                if (Arith.compare(data.getDouble(RefundInstockEnum.TOTAL_MONEY.getAttr()), 0) < 0) {
                    throw new XbbException(InstockErrorCodeEnum.API_ERROR_210048.getCode(), InstockErrorCodeEnum.API_ERROR_210048.getMsg());
                }
                JSONArray refundArr = data.getJSONArray(RefundInstockEnum.REF_ID.getAttr());
                Long refundId = refundArr.getJSONObject(0).getLong("id");
                RefundEntityExt refundEntityExt = refundModel.getByKey(refundId, corpid);
                PaasFormDataEsListVO instockListVO = saasInstockHelp.getInstockListFromEs(corpid, refundId, InstockTypeEnum.RED_CONTRACT_INSTOCK.getCode());
                verifyContractTotalMoney(data, dataId, refundEntityExt, instockListVO);
                break;
            default:
                break;
        }
        Long date = data.getLong(InstockEnum.TIME.getAttr());
        if (Objects.isNull(date)) {
            throw new XbbException(InstockErrorCodeEnum.API_ERROR_210042);
        }
        //结账中校验
        String redisHelperValue = paasRedisHelper.getValue(RedisPrefixConstant.SETTLE_ACCOUNTS, corpid);
        if (StringUtil.isNotEmpty(redisHelperValue)) {
            if (date <= DateTimeUtil.getTodayEndInt(Long.valueOf(redisHelperValue))){
                throw new XbbException(SettleAccountErrorCodeEnum.API_ERROR_254007);
            }
        }
        SettleAccountsEntity accountsEntity = settleAccountsModel.getLastSettleAccounts(corpid);
        InstockEntityExt instockEntityExt = null;
        if (dataId != null && !Objects.equals(dataId, 0L)) {
            //判断是否是审批编辑
            if (Objects.isNull(taskId)){
                //校验日期是否受结账限制
                if (Objects.nonNull(accountsEntity) && Objects.nonNull(accountsEntity.getCheckOutTime())) {
                    if (date <= DateTimeUtil.getTodayEndInt(accountsEntity.getCheckOutTime())) {
                        throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212051);
                    }
                }
            }
            instockEntityExt = instockModel.getByKey(dataId, corpid);
        } else {
            String value = paasRedisHelper.getValue(RedisPrefixConstant.PRODUCE_STOCK_FLOW, corpid);
            if (StringUtil.isNotEmpty(value)) {
                throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212038);
            }
            //校验日期是否受结账限制
            if (Objects.nonNull(accountsEntity) && Objects.nonNull(accountsEntity.getCheckOutTime())) {
                if (date <= DateTimeUtil.getTodayEndInt(accountsEntity.getCheckOutTime())) {
                    throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212033);
                }
            }
        }
        Map<String, FieldAttrEntity> explainMap = validateDataDTO.getExplainMap();

        // ==============数据格式校验===================
        SaveFormatHelp.formatLinkBusiness4Save(data, InstockEnum.WAREHOUSE_ID.getAttr(), InstockEnum.WAREHOUSE_ID_LINKED_TEXT.getAttr(), InstockEnum.WAREHOUSE_ID.getFieldType());
        if (data.getJSONArray(InstockEnum.REF_ID.getAttr()) == null || data.getJSONArray(InstockEnum.REF_ID.getAttr()).size() == 0) {
            data.remove(InstockEnum.REF_ID.getAttr());
        }
        SaveFormatHelp.formatLinkBusiness4Save(data, InstockEnum.REF_ID.getAttr(), InstockEnum.REF_ID_LINKED_TEXT.getAttr(), InstockEnum.REF_ID.getFieldType());

        //保存前 批次号必填校验
        SaveFormatHelp.batchSaveRequiredCheck(validateDataDTO);
        // 校验编号长度不超过100且编号存在
        FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
        FieldAttrEntity sheetNo = explainMap.getOrDefault(InstockEnum.SHEET_NO.getAttr(), fieldAttrEntity);
        if (Objects.nonNull(sheetNo) && Objects.equals(sheetNo.getStrictController(), BasicConstant.ZERO)) {
            ValidateUtil.checkSerialNumber(validateDataDTO.getSerialNo(), Objects.toString(sheetNo.getAttrName(), ""));
        }

        FieldAttrEntity memoField = explainMap.getOrDefault(InstockEnum.MEMO.getAttr(), fieldAttrEntity);
        // 校验备注长度
        ValidateUtil.checkFieldEntityLength(data, InstockEnum.MEMO.getAttr(), Objects.toString(memoField.getAttrName(), ""), AttrLenthConstant.MEMO_MAX_LENGTH);

        // ==============业务逻辑校验===================
        // 入库单特殊逻辑校验
        // 注意：这里的校验是基于上面关联业务校验之后做的，data格式已经和前端传入的不同，不能和formatLinkBusiness4Save调换顺序
        checkInstock(data, instockEntityExt);
        boolean isNew = analyticalHelp.isNew(dataId, taskId, corpid);
        // 入库单产品校验。编辑时不校验，因为编辑时不能修改产品
        checkInstockProduct2(data, corpid, dataId, taskId, isNew);

        validateDataDTO.setData(data);
    }

    /**
     * 入库金额校验
     * @param data
     * @param refundEntityExt
     * @param instockListVO
     * @param dataId
     * @throws XbbException
     * @author xinpeng.jiang
     * @date 2020/12/4 9:54
     * @version 1.0
     */
    private void verifyContractTotalMoney(JSONObject data, Long dataId, RefundEntityExt refundEntityExt, PaasFormDataEsListVO instockListVO) throws XbbException {
        Double refundMoney = refundEntityExt.getData().getDouble(RefundEnum.RETURN_AMOUNT.getAttr());
        double refundtInstockTotalMoney = 0D;
        // 新建时附初值，编辑跳过
        if (dataId == null) {
            refundtInstockTotalMoney = data.getDoubleValue(RefundInstockEnum.TOTAL_MONEY.getAttr());
        }
        // 计算入库单总金额
        if (instockListVO == null) {
            throw new XbbException(ContractErrorCodeEnum.API_ERROR_203001.getCode(), ContractErrorCodeEnum.API_ERROR_203001.getMsg());
        }
        List<PaasFormDataEntityExt> instockList = instockListVO.getPaasFormDataESList();
        for (PaasFormDataEntityExt instock : instockList) {
            JSONObject refundInstockData = instock.getData();
            Double refundInstockMoney = refundInstockData.getDouble(RefundInstockEnum.TOTAL_MONEY.getAttr());
            if (Objects.nonNull(refundInstockMoney)) {
                refundtInstockTotalMoney = Arith.add(refundtInstockTotalMoney, refundInstockMoney);
            }
        }
        // 和退款单金额比较
        if (Objects.nonNull(refundMoney) && Arith.compare(refundtInstockTotalMoney, refundMoney) > 0) {
            throw new XbbException(InstockErrorCodeEnum.API_ERROR_210049.getCode(), InstockErrorCodeEnum.API_ERROR_210049.getMsg());
        }
    }

    private void checkInstockProduct2(JSONObject dataList, String corpid, Long dataId, Long taskId, boolean isNew) throws XbbException {
        // 关联单据id
        Long refId = dataList.getLong(InstockEnum.REF_ID.getAttr ());
        Integer type = dataList.getInteger(InstockEnum.TYPE.getAttr());
        if (Objects.isNull(refId) && !Objects.equals(type, InstockTypeEnum.OTHER_INSTOCK.getCode())) {
            throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212019);
        }
        // 入库标志
        JSONArray productArray = FastJsonHelper.getJsonArrOrDefaultFromFormData(dataList,InstockEnum.PRODUCT.getAttr(),new JSONArray());
        // 先进行统一的产品数量仓库等校验
        if (productArray == null || productArray.isEmpty()) {
            throw new XbbException(InstockErrorCodeEnum.API_ERROR_210011);
        }

        List<Long> pageProductIds = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Double> productNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 是否需要合并
        boolean needMerge = false;
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject productObj = productArray.getJSONObject(i);
            Long refProductId = productObj.getLong(BusinessConstant.REF_PRODUCT_ID);
            if (Objects.isNull(refProductId)) {
                needMerge = true;
                break;
            }
        }
        for (int i = 0; i < productArray.size(); i++) {
            JSONObject productObj = productArray.getJSONObject(i);
            Long productId = productObj.getLong(SelectProductEnum.PRODUCT.getAttr());
            pageProductIds.add(productId);
            Double productNum = productObj.getDouble(SelectProductEnum.NUM.getAttr());
            String key;
            if (needMerge) {
                key = productId.toString();
            } else {
                Long refProductId = productObj.getLong(BusinessConstant.REF_PRODUCT_ID);
                key = productId + "_" + refProductId;
            }
            Double num = productNumMap.getOrDefault(key,0D);
            num = Arith.add(num, productNum);
            productNumMap.put(key,num);
        }

        if (isNew) {
            //TODO 未来批次序列强校验
            Map<Long,String> pageProductMap = saasProductHelp.productExit(corpid,pageProductIds);
            InstockTypeEnum instockTypeEnum = InstockTypeEnum.getByCode(type);
            switch (instockTypeEnum){
                case PURCHASE_INSTOCK:
                case RETURNED_MATERIEL_INSTOCK:
                case TRANSFER_INSTOCK:
                    Map<String,Double> unInstockMap = saasProductHelp.getUnInstockProduct(corpid,refId,type,needMerge, taskId, dataId);
                    ProductUtil.stockProductNumLegalCheck(false, unInstockMap,productNumMap,pageProductMap,I18nMessageUtil.getMessage(ErrorMessageConstant.INTO_OF_STOCK_ERROR));
                    break;
                case INVENTORY_INSTOCK:
                    InventoryEntityExt inventoryEntityExt = inventoryModel.getByKey(refId, corpid);
                    JSONObject data = inventoryEntityExt.getData();
                    if (Objects.nonNull(data.get(InventoryEnum.INSTOCK_ID.getAttr())) && !Objects.equals(data.getLong(InventoryEnum.INSTOCK_ID.getAttr()), 0L)) {
                        throw new XbbException(InstockErrorCodeEnum.API_ERROR_210040, String.format(InstockErrorCodeEnum.API_ERROR_210040.getMsg(), inventoryEntityExt.getSerialNo()));
                    }
                    unInstockMap = saasProductHelp.getUnInstockProduct(corpid,refId,type,needMerge, taskId, dataId);
                    ProductUtil.stockProductNumLegalCheck(false, unInstockMap,productNumMap,pageProductMap,I18nMessageUtil.getMessage(ErrorMessageConstant.INTO_OF_STOCK_ERROR));
                    break;
                case RED_CONTRACT_INSTOCK:
                    // TODO 这里需要看看
                    unInstockMap = saasProductHelp.getRefundUnInstockProduct(corpid,refId,type, needMerge);
                    ProductUtil.stockProductNumLegalCheck(false, unInstockMap,productNumMap,pageProductMap,"大于剩余未入库数量");
                    break;
                case ASSEMBLE_INSTOCK:
                    break;
                case FINSHED_PRODUCT_INSTOCK:
                    break;
                case OTHER_INSTOCK:
                    break;
                default:
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, "入库类型错误");

            }
        }else {
            //TODO 精度变化导致数量变化怎么办
        }
    }
    
    /**
     * 校验入库单各字段
     *
     * @param dataList         前端传入的dataList
     * @param instockEntityExt 入库单实体，如果为null则为新建，不为null则为编辑
     * @return void
     * @author chaoxiong.lei
     * @Date 20:05 2019/3/1
     * @since v1.0
     **/
    private void checkInstock(JSONObject dataList, InstockEntityExt instockEntityExt) throws XbbException {
        if (dataList == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028);
        }
        if (instockEntityExt == null) {
            // 新建入库单不需要进行下面的校验
            return;
        }
        try {
            // inComing标志
            JSONObject oriDataList = instockEntityExt.getData();
//            Integer incoming = oriDataList.getInteger(InstockEnum.INCOMING.getAttr());
            Long dataId = instockEntityExt.getId();
            // 改为判断id是否存在
            if (Objects.nonNull(dataId)) {
                // 入库仓库校验
                Long warehouseId = dataList.getLong(InstockEnum.WAREHOUSE_ID.getAttr());

                // 实际出过库的情况下，入库仓库不允许修改
                if (!Objects.equals(oriDataList.getLong(InstockEnum.WAREHOUSE_ID.getAttr()), warehouseId) && FlowStatusEnum.getStatisticType().contains(instockEntityExt.getFlowStatus())) {
                    throw new XbbException(InstockErrorCodeEnum.API_ERROR_210009);
                }

                // 入库类型校验，实际入过库的情况下，入库类型不允许修改
                Integer type = dataList.getInteger(InstockEnum.TYPE.getAttr());
                if (!Objects.equals(type, oriDataList.getInteger(InstockEnum.TYPE.getAttr()))) {
                    throw new XbbException(InstockErrorCodeEnum.API_ERROR_210007);
                }

                // 实际出过库的情况下，关联单据不允许修改
                Long refId = dataList.getLong(InstockEnum.REF_ID.getAttr());
                if (!Objects.equals(refId, oriDataList.getLong(InstockEnum.REF_ID.getAttr()))) {
                    throw new XbbException(InstockErrorCodeEnum.API_ERROR_210008);
                }
            }
        } catch (Exception e) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100015);
        }
    }
}
